package com.xiaojie.summer.application;


import com.xiaojie.persistence.core.manager.TransactionalManager;
import com.xiaojie.persistence.core.session.DefaultSqlSessionFactory;
import com.xiaojie.persistence.core.session.SqlSessionFactory;
import com.xiaojie.summer.SummerServletContextHandler;
import com.xiaojie.summer.annotation.*;
import com.xiaojie.summer.annotation.*;
import com.xiaojie.summer.factory.WebApplicationContext;
import com.xiaojie.summer.resolver.ClassResolver;
import com.xiaojie.summer.server.JettyServer;


import javax.annotation.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;


/**
 * @author Nick
 * @date 2021/5/22
 * @description 启动类
 */
public class SummerApplicationStarter {

    private static WebApplicationContext applicationContext = new WebApplicationContext();

    public static WebApplicationContext run(Class loadClass, String[] args) {
        // 加载配置文件 application.properties
        Properties properties = new Properties();
        // 使用ClassLoader加载properties配置文件生成对应的输入流
        InputStream in = SummerApplicationStarter.class.getClassLoader().getResourceAsStream("application.properties");
        // 使用properties对象加载输入流
        try {
            properties.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 应用 classpath 路径
        String classPath = loadClass.getClassLoader().getResource(".").getPath();
        properties.setProperty("classPath", classPath);

        // 设置 Properties
        applicationContext.setProperties(properties);


        // 进行扫描它包下所有的类
        // 加载所有的类
        ClassResolver classResolver = new ClassResolver();
        List<Class> classList = null;
        try {
            classList = classResolver.loadAllClassListOnTopClass(loadClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        Map<String, Object> nameBeanMap = applicationContext.getNameBeanMap();
        Map<Class, Object> classBeanMap = applicationContext.getClassBeanMap();

        Map<String, Object> tempNameBeanMap = applicationContext.getTempNameBeanMap();
        Map<Class, Object> tempClassBeanMap = applicationContext.getTempClassBeanMap();

        // 遍历所有的类,将他们添加到临时的map
        for (Class clazz : classList) {

            if (!hasComponentAnnotation(clazz)) {
                continue;
            }

            String clazzName = clazz.getName();
            String objectName = toLowerCaseFirstOne(clazzName.substring(clazzName.lastIndexOf(".") + 1));
            // 注意区别Service的value
            if (clazz.isAnnotationPresent(Service.class)) {
                Service service = (Service) clazz.getAnnotation(Service.class);
                String theServiceName = service.value();
                if (theServiceName != null && !"".equals(theServiceName)) {
                    objectName = theServiceName;
                }
            }


            Object object = null;
            try {
                object = clazz.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }

            tempClassBeanMap.put(clazz, object);
            tempNameBeanMap.put(objectName, object);
        }

        // 加载注入器之前的操作, 获取 Configuration 配置类中的Bean对象,添加到对象池子
        for (Class clazz : tempClassBeanMap.keySet()) {
            Object configurationObject = null;
            try {
                configurationObject = clazz.newInstance();
                for (Method method : clazz.getMethods()) {
                    // 扫描Bean注解的方法
                    if (method.isAnnotationPresent(Bean.class)) {

                        Object object = method.invoke(configurationObject, properties);
                        String clazzName = object.getClass().getName();
                        String objectName = toLowerCaseFirstOne(clazzName.substring(clazzName.lastIndexOf(".") + 1));
                        // 注意区别Service的value
                        if (clazz.isAnnotationPresent(Service.class)) {
                            Service service = (Service) clazz.getAnnotation(Service.class);
                            String theServiceName = service.value();
                            if (theServiceName != null && theServiceName != "") {
                                objectName = theServiceName;
                            }
                        }
                        tempNameBeanMap.put(objectName, object);
                        // 作为Bean注解产生的对象,不需要二次设置值,直接进最终的池子
                        nameBeanMap.put(objectName, object);
                        classBeanMap.put(object.getClass(), object);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 把tempNameBeanMap的对象同步到  tempClassBeanMap
        for (Object o : tempNameBeanMap.values()) {
            tempClassBeanMap.put(o.getClass(), o);
        }

        // 这里应该是后置处理器的,来不及了.我直接在这里执行,将mapper解析出来,加到对象池子
        for (Class clazz : classList) {
            if (clazz.isAnnotationPresent(Mapper.class)) {
                SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) applicationContext.getBean(SqlSessionFactory.class);
                if (sqlSessionFactory != null) {
                    Object mapper = sqlSessionFactory.openSqlSession().getMapper(clazz);
                    String clazzName = clazz.getName();
                    String objectName = toLowerCaseFirstOne(clazzName.substring(clazzName.lastIndexOf(".") + 1));
                    // 注意区别Service的value
                    if (clazz.isAnnotationPresent(Service.class)) {
                        Service service = (Service) clazz.getAnnotation(Service.class);
                        String theServiceName = service.value();
                        if (theServiceName != null && theServiceName != "") {
                            objectName = theServiceName;
                        }
                    }
                    tempNameBeanMap.put(objectName, mapper);
                    tempClassBeanMap.put(clazz, mapper);
                    // 作为Mapper注解产生的对象,也不需要二次设置值,直接进最终的池子 -- 这里应该是在后置处理器中处理的
                    nameBeanMap.put(objectName, mapper);
                    classBeanMap.put(clazz, mapper);
                }
            }
        }

        // 开始依赖注入
        for (Class clazz : tempClassBeanMap.keySet()) {
            Object thisObject = tempClassBeanMap.get(clazz);
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    String fieldName = field.getName();

                    Object o = tempNameBeanMap.get(fieldName);
                    field.setAccessible(true);
                    try {
                        field.set(thisObject, o);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
            // 将对象放到正式的池子
            String clazzName = clazz.getName();
            String objectName = toLowerCaseFirstOne(clazzName.substring(clazzName.lastIndexOf(".") + 1));
            // 注意区别Service的value
            if (clazz.isAnnotationPresent(Service.class)) {
                Service service = (Service) clazz.getAnnotation(Service.class);
                String theServiceName = service.value();
                if (theServiceName != null && theServiceName != "") {
                    objectName = theServiceName;
                }
            }
            nameBeanMap.put(objectName, thisObject);
            classBeanMap.put(clazz, thisObject);
        }

        System.out.println(nameBeanMap);
        System.out.println(classBeanMap);

        // 设置事务处理
        Map<String, Object> txMap = new HashMap<>();
        for (Map.Entry<String, Object> entry : nameBeanMap.entrySet()) {
            Class clazz = entry.getValue().getClass();
            // 如果有事务处理的话,则需要配置事务代理类
            if (clazz.isAnnotationPresent(Transactional.class)) {
                Object proxyInstance = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 拿到事务,
//                        defaultSqlSessionFactory
                        DefaultSqlSessionFactory sqlSessionFactory = (DefaultSqlSessionFactory) nameBeanMap.get("defaultSqlSessionFactory");
                        TransactionalManager transactionalManager = sqlSessionFactory.getConfiguration().getTransactionalManager();
                        transactionalManager.startTransactional();
                        // 执行业务
                        Object result = null;
                        try {
                            result = method.invoke(proxy, args);
                            // 没问题,提交
                            transactionalManager.commit();
                        } catch (Exception e) {
                            // 有问题回滚
                            transactionalManager.rollback();
                        }

                        return result;
                    }
                });
                txMap.put(entry.getKey(), entry.getValue());
            }
        }
        for (Map.Entry<String, Object> entry : txMap.entrySet()) {
            nameBeanMap.put(entry.getKey(), entry.getValue());
            classBeanMap.put(entry.getValue().getClass(), entry.getValue());
        }


        // 设置 服务器请求映射
        Map<String, String> requestMap = applicationContext.getRequestMap();
        for (Map.Entry<String, Object> entry : nameBeanMap.entrySet()) {
            String classPathMapping = "";
            String methodPathMapping = "";
            String objectName = entry.getKey();
            Object thisObject = entry.getValue();
            Class clazz = thisObject.getClass();
            if (clazz.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping service = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
                String mappingPath = service.value();
                if (mappingPath != null && mappingPath != "") {
                    classPathMapping = mappingPath;
                }
            }


            for (Method method : clazz.getMethods()) {
                // 扫描Bean注解的方法
                if (method.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping service = (RequestMapping) method.getAnnotation(RequestMapping.class);
                    String mappingPath = service.value();
                    if (mappingPath != null && mappingPath != "") {
                        methodPathMapping = mappingPath;
                        String realPathMapping = classPathMapping + methodPathMapping;
                        requestMap.put(realPathMapping, objectName + "." + method.getName());
                    }
                }
            }
        }

        // ----- 服务启动 ------
        JettyServer jettyServer = new JettyServer();
        jettyServer.startServer(loadClass, new SummerServletContextHandler(applicationContext));

        return applicationContext;
    }

    /**
     * 首字母小写
     *
     * @param s
     * @return
     */
    public static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
        }
    }

    /**
     * 判断是否有 @Component 注解
     *
     * @param classz
     * @return
     */
    public static boolean hasComponentAnnotation(Class<?> classz) {
        Annotation[] annotations = classz.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation.annotationType() != Deprecated.class &&
                    annotation.annotationType() != SuppressWarnings.class &&
                    annotation.annotationType() != Override.class &&
                    annotation.annotationType() != PostConstruct.class &&
                    annotation.annotationType() != PreDestroy.class &&
                    annotation.annotationType() != Resource.class &&
                    annotation.annotationType() != Resources.class &&
                    annotation.annotationType() != Generated.class &&
                    annotation.annotationType() != Target.class &&
                    annotation.annotationType() != Retention.class &&
                    annotation.annotationType() != Documented.class &&
                    annotation.annotationType() != Inherited.class
            ) {
                if (annotation.annotationType() == Component.class) {
                    return true;
                } else {
                    if (hasComponentAnnotation(annotation.annotationType())) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

}
